package com.nf.teamwork.mall.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.UUID;

/**
 * @Author: LJP
 * @Classname JwtUtil
 * @Date: 2020-02-29 19:18
 * @Description: JWT相关工具类
 */
public class JwtUtil {

    private static final String JWT_PAYLOAD_USER_KEY = "user";

    /**
     * 生成token的方法
     * 私钥加密token
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥、公钥
     * @param expire     过期时间，单位分钟
     * @return JWT
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
        /**
         * claim()：用于放置一个Map对象，
         *          JWT_PAYLOAD_USER_KEY就是KEY值，
         *          JsonUtil.toString(userInfo)：VALUE值，其实就是拿到一个对象，这里是拿到user对象，
         *                                        然后通过JsonUtils.toString来将JavaBean转换成json格式的字符串
         * setId()：用来设置JWT的id，通过createJTI()方法获取到UUID随机生成的id
         * setExpiration()：设置过期时间
         * signWith()：设置签名方式(也就是需要以什么方式去加密)，
         *             这里设置的是RS256加密方式，其实可以不用设置加密方式
         *             因为现在使用的是RSA非对称加密，而RSA非对称加密使用的加密方式默认就是RS256
         */
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo))
                .setId(createJTI())
                .setExpiration(DateTime.now().plusMinutes(expire).toDate())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expire     过期时间，单位秒
     * @return JWT
     */
    public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo))
                .setId(createJTI())
                .setExpiration(DateTime.now().plusSeconds(expire).toDate())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 公钥解析token
     * 验证token是否正确
     * @param token     用户请求中的token
     * @param publicKey 公钥
     * @return Jws<Claims>
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    /**
     * 通过Base64加密方式放回一条UUID对象随机生成的id
     * @return 随机生成的id
     */
    private static String createJTI() {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /**
     * 获取到token的载荷部分
     * 获取token中的用户信息（解析token的方法）
     * 公钥解密
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 封装的一个对象，类型就是由userType参数来决定的
     */
    public static <T> PayLoad<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        PayLoad<T> claims = new PayLoad<>();
        claims.setId(body.getId());
        claims.setUserInfo(JsonUtil.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
        claims.setExpiration(body.getExpiration());
        return claims;
    }

    /**
     * 获取token中的载荷信息（解析token的方法）
     * 公钥解密
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     */
    public static <T> PayLoad<T> getInfoFromToken(String token, PublicKey publicKey) {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        PayLoad<T> claims = new PayLoad<>();
        claims.setId(body.getId());
        claims.setExpiration(body.getExpiration());
        return claims;
    }
}
